package edu.cornell.cs4740.wsd;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

import edu.cornell.cs4740.wsd.parsing.TrainingData;
import edu.cornell.cs4740.wsd.predictions.BaselinePredictor;
import edu.cornell.cs4740.wsd.tools.MulanFormat;

public class WordSenseDisamiguation {

  /**
   * @param args
   *          ars[0] is the training data path, args[1] is the test data path,
   *          and args[2] is the number of folds of cross validation
   * @throws Exception
   */
  public static void main(String[] args) throws Exception {

    if (args[0].equals("find")) {
      findBestFeatures();
      return;
    }
    String trainingPath = args[0];
    String testPath = args[1];

    // arg 3 is the number of folds wanted in validation
    int sizeOfEvaluationSet;
    int numFolds = Integer.parseInt(args[2]);

    System.out.println("Loading training data from:");
    System.out.println(trainingPath);
    System.out.println("Loading test data from:");
    System.out.println(testPath);

    // ------- READ IN TRAINING DATA
    List<TrainingData> trainingDataFull = new ArrayList<TrainingData>();

    try {
      Scanner trainingScanner = new Scanner(new File(trainingPath));
      while (trainingScanner.hasNext()) {
        trainingDataFull
            .add(new TrainingData(trainingScanner.nextLine(), false));
      }
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (Exception e) {
      e.printStackTrace();
    }

    System.out.println("training data read");

    // ----- READ IN TEST DATA -------

    // test data is identical to training data with 0s for the sense
    // information
    List<TrainingData> testData = new ArrayList<TrainingData>();

    try {
      Scanner testScanner = new Scanner(new File(testPath));
      while (testScanner.hasNext()) {
        testData.add(new TrainingData(testScanner.nextLine(), false));
      }
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (Exception e) {
      e.printStackTrace();
    }

    System.out.println("Test data read");

    sizeOfEvaluationSet = trainingDataFull.size() / numFolds;

    // turn training, evaluation, and test data into maps of examples of
    // words so we can
    // send them through Mulan
    Map<String, List<TrainingData>> trainingDataFullMap = new HashMap<String, List<TrainingData>>();
    Map<String, List<TrainingData>> testDataMap = new HashMap<String, List<TrainingData>>();

    for (TrainingData td : trainingDataFull) {
      List<TrainingData> tdList = trainingDataFullMap.get(td.getWord()
          .getWord());
      if (tdList == null) {
        tdList = new ArrayList<TrainingData>();
        tdList.add(td);
      } else {
        tdList.add(td);
      }
      trainingDataFullMap.put(td.getWord().getWord(), tdList);
    }

    for (TrainingData td : testData) {
      List<TrainingData> tdList = testDataMap.get(td.getWord().getWord());
      if (tdList == null) {
        tdList = new ArrayList<TrainingData>();
        tdList.add(td);
      } else {
        tdList.add(td);
      }
      testDataMap.put(td.getWord().getWord(), tdList);
    }

    // this can be put in a for loop to vary the window size

    int windowSize = -1; // number of words before the target and after the
    // target (x2 words total)

    double[][] performances = new double[0][0];
    double[][] performancesKaggleStyle = new double[0][0];

    String[] featureSetClassificationMethods = new String[] {
        "uniRPredictions", "uniMPredictions", "biRPredictions",
        "biMPredictions", "uniStemRPredictions", "uniStemMPredictions",
        "biStemRPredictions", "biStemMPredictions" };

    // row index is the windowsize-5
    // col index is the type of feature set classification method (ie.
    // uniStemRPredictions)
    // each entry is the average performance accross folds for that feature
    // set classification at that window size
    double[][] averagePerformances = new double[6][featureSetClassificationMethods.length];
    double[][] averageKagglePerformances = new double[6][featureSetClassificationMethods.length];
    for (int ws = 5; ws <= 10; ws++) {

      windowSize = ws;

      // col index is the type of feature set classification method (ie.
      // uniStemRPredictions)
      // row index is the fold index
      // each entry is the performance of that feature set classification
      // on
      // that fold
      performances = new double[numFolds][featureSetClassificationMethods.length];
      performancesKaggleStyle = new double[numFolds][featureSetClassificationMethods.length];

      for (int i = 0; i < numFolds; i++) {

        // ----- CREATE EVALUATION SET ------

        Collections.shuffle(trainingDataFull);
        List<TrainingData> evaluationData = trainingDataFull.subList(0,
            sizeOfEvaluationSet);

        // everything else becomes the real training data
        List<TrainingData> trainingData = trainingDataFull.subList(
            sizeOfEvaluationSet, trainingDataFull.size());

        // sanity check to make sure we aren't missing stuff
        System.out.println(trainingData.size() + " training data samples");
        System.out.println(evaluationData.size() + " evaluation data samples");

        Map<String, List<TrainingData>> evaluationDataMap = new HashMap<String, List<TrainingData>>();
        Map<String, List<TrainingData>> trainingDataMap = new HashMap<String, List<TrainingData>>();

        for (TrainingData td : trainingData) {
          List<TrainingData> tdList = trainingDataMap.get(td.getWord()
              .getWord());
          if (tdList == null) {
            tdList = new ArrayList<TrainingData>();
            tdList.add(td);
          } else {
            tdList.add(td);
          }
          trainingDataMap.put(td.getWord().getWord(), tdList);
        }

        for (TrainingData td : evaluationData) {
          List<TrainingData> tdList = evaluationDataMap.get(td.getWord()
              .getWord());
          if (tdList == null) {
            tdList = new ArrayList<TrainingData>();
            tdList.add(td);
          } else {
            tdList.add(td);
          }
          evaluationDataMap.put(td.getWord().getWord(), tdList);
        }

        // --------- RUN BASELINE ---------

        BaselinePredictor baselinePredictor = new BaselinePredictor(
            trainingDataMap);

        List<Double> vectorDistances = new ArrayList<Double>();
        double numErrors = 0;
        double total = 0;
        for (TrainingData td : evaluationData) {
          List<Boolean> prediction = baselinePredictor.predict(td);
          List<Boolean> actual = td.getSenseUsage();
          List<Double> normPredVect = EvaluationTools
              .convertSensesToNormalizedVector(prediction);
          List<Double> normActVect = EvaluationTools
              .convertSensesToNormalizedVector(actual);
          double distance = EvaluationTools.distBetweenVectors(normPredVect,
              normActVect);
          vectorDistances.add(new Double(distance));

          for (int u = 0; u < prediction.size(); u++) {
            if (!(prediction.get(u).equals(actual.get(u)))) {
              numErrors++;
            }

            total++;
          }
        }

        int totalDist = 0;
        for (Double d : vectorDistances) {
          totalDist += d.doubleValue();
        }

        System.out.println("Baseline error rate: " + numErrors / total);

        System.out.println("Baseline average distance: " + ((double) totalDist)
            / ((double) vectorDistances.size()));

        // --------- END BASELINE ---------

        List<Map<String, List<List<Boolean>>>> predictions = new ArrayList<Map<String, List<List<Boolean>>>>();

        // unigrams unstemmed
        MulanFormat mulanUnigrams = new MulanFormat(trainingDataMap,
            windowSize, false, false);
        // word, list of predicted senses for each example in the order
        // given
        Map<String, List<List<Boolean>>> uniRPredictions = mulanUnigrams
            .getPredictions(evaluationDataMap, MulanFormat.RAKEL);
        predictions.add(uniRPredictions);
        Map<String, List<List<Boolean>>> uniMPredictions = mulanUnigrams
            .getPredictions(evaluationDataMap, MulanFormat.MLKNN);
        predictions.add(uniMPredictions);

        // bigrams unstemmed
        MulanFormat mulanBigrams = new MulanFormat(trainingDataMap, windowSize,
            true, false);
        // word, list of predicted senses for each example in the order
        // given
        Map<String, List<List<Boolean>>> biRPredictions = mulanBigrams
            .getPredictions(evaluationDataMap, MulanFormat.RAKEL);
        predictions.add(biRPredictions);
        Map<String, List<List<Boolean>>> biMPredictions = mulanBigrams
            .getPredictions(evaluationDataMap, MulanFormat.MLKNN);
        predictions.add(biMPredictions);

        // unigrams stemmed
        MulanFormat mulanUnigramsStem = new MulanFormat(trainingDataMap,
            windowSize, false, false);
        // word, list of predicted senses for each example in the order
        // given
        Map<String, List<List<Boolean>>> uniStemRPredictions = mulanUnigramsStem
            .getPredictions(evaluationDataMap, MulanFormat.RAKEL);
        predictions.add(uniStemRPredictions);
        Map<String, List<List<Boolean>>> uniStemMPredictions = mulanUnigramsStem
            .getPredictions(evaluationDataMap, MulanFormat.MLKNN);
        predictions.add(uniStemMPredictions);

        // bigrams stemmed
        MulanFormat mulanBigramsStem = new MulanFormat(trainingDataMap,
            windowSize, true, false);
        // word, list of predicted senses for each example in the order
        // given
        Map<String, List<List<Boolean>>> biStemRPredictions = mulanBigramsStem
            .getPredictions(evaluationDataMap, MulanFormat.RAKEL);
        predictions.add(biStemRPredictions);
        Map<String, List<List<Boolean>>> biStemMPredictions = mulanBigramsStem
            .getPredictions(evaluationDataMap, MulanFormat.MLKNN);
        predictions.add(biStemMPredictions);

        performances[i] = EvaluationTools.getPerformances(predictions,
            evaluationDataMap);
        performancesKaggleStyle[i] = EvaluationTools.getKagglePerformances(
            predictions, evaluationDataMap);
      }

      averagePerformances[ws - 5] = EvaluationTools
          .averageOverFolds(performances);
      averageKagglePerformances[ws - 5] = EvaluationTools
          .averageOverFolds(performancesKaggleStyle);
      for (int i = 0; i < averagePerformances.length; i++) {
        System.out.println("Distance: WindowSize of " + ws + " with "
            + featureSetClassificationMethods[i] + " = "
            + averagePerformances[ws - 5][i]);
        System.out.println("Kaggle Style: WindowSize of " + ws + " with "
            + featureSetClassificationMethods[i] + " = "
            + averageKagglePerformances[ws - 5][i]);
      }

    }

    double best = Double.MAX_VALUE;
    int bestIndexI = 0;
    int bestIndexJ = 0;
    for (int i = 0; i < averagePerformances.length; i++) {
      for (int j = 0; j < averagePerformances[0].length; j++) {
        if (averagePerformances[i][j] < best) {
          best = averagePerformances[i][j];
          bestIndexI = i;
          bestIndexJ = j;
        }
      }
    }

    MulanFormat mulan;
    Map<String, List<List<Boolean>>> predictions;
    windowSize = bestIndexI + 5;
    switch (bestIndexJ) {
    case 0:
      // uniRPredictions
      mulan = new MulanFormat(trainingDataFullMap, windowSize, false, false);
      predictions = mulan.getPredictions(testDataMap, MulanFormat.RAKEL);
      break;
    case 1:
      // uniMPredictions
      mulan = new MulanFormat(trainingDataFullMap, windowSize, false, false);
      predictions = mulan.getPredictions(testDataMap, MulanFormat.MLKNN);
      break;
    case 2:
      // biRPredictions
      mulan = new MulanFormat(trainingDataFullMap, windowSize, true, false);
      predictions = mulan.getPredictions(testDataMap, MulanFormat.RAKEL);
      break;
    case 3:
      // biMPredictions
      mulan = new MulanFormat(trainingDataFullMap, windowSize, true, false);
      predictions = mulan.getPredictions(testDataMap, MulanFormat.MLKNN);
      break;
    case 4:
      // uniStemRPredictions
      mulan = new MulanFormat(trainingDataFullMap, windowSize, false, true);
      predictions = mulan.getPredictions(testDataMap, MulanFormat.RAKEL);
      break;
    case 5:
      // uniStemMPredictions
      mulan = new MulanFormat(trainingDataFullMap, windowSize, false, true);
      predictions = mulan.getPredictions(testDataMap, MulanFormat.MLKNN);
      break;
    case 6:
      // biStemRPredictions
      mulan = new MulanFormat(trainingDataFullMap, windowSize, true, true);
      predictions = mulan.getPredictions(testDataMap, MulanFormat.RAKEL);
      break;
    case 7:
      // biStemMPredictions
      mulan = new MulanFormat(trainingDataFullMap, windowSize, true, true);
      predictions = mulan.getPredictions(testDataMap, MulanFormat.MLKNN);
      break;
    default:
      mulan = new MulanFormat(trainingDataFullMap, windowSize, true, true);
      predictions = mulan.getPredictions(testDataMap, MulanFormat.MLKNN);
      break;
    }

    createKaggleOutput(predictions, testData);

  }

  public static void findBestFeatures() throws Exception {

    String[] names = { "activate", "add", "appear", "argument", "arm", "ask",
        "atmosphere", "audience", "bank", "begin", "climb", "decide", "degree",
        "difference", "different", "difficulty", "disc", "eat", "encounter",
        "expect", "express", "hear", "hot", "image", "important", "interest",
        "judgment", "lose", "mean", "miss", "note", "operate", "organization",
        "paper", "party", "performance", "plan", "play", "produce", "provide",
        "receive", "remain", "rule", "shelter", "simple", "smell", "solid",
        "sort", "source", "suspend", "talk", "treat", "use", "wash", "watch",
        "win", "write", };
    for (int j = 0; j < names.length; j++) {
      String trainingPath = "wsd-data/" + names[j] + ".txt";
      String testPath = "wsd-data/" + names[j] + ".test";

      // arg 3 is the number of folds wanted in validation
      int sizeOfEvaluationSet;
      int numFolds = 1;

      System.out.println("Loading training data from:");
      System.out.println(trainingPath);
      System.out.println("Loading test data from:");
      System.out.println(testPath);

      // ------- READ IN TRAINING DATA
      List<TrainingData> trainingDataFull = new ArrayList<TrainingData>();

      try {
        Scanner trainingScanner = new Scanner(new File(trainingPath));
        while (trainingScanner.hasNext()) {
          trainingDataFull.add(new TrainingData(trainingScanner.nextLine(),
              false));
        }
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      } catch (Exception e) {
        e.printStackTrace();
      }

      System.out.println("training data read");

      // ----- READ IN TEST DATA -------

      // test data is identical to training data with 0s for the sense
      // information
      List<TrainingData> testData = new ArrayList<TrainingData>();

      try {
        Scanner testScanner = new Scanner(new File(testPath));
        while (testScanner.hasNext()) {
          testData.add(new TrainingData(testScanner.nextLine(), false));
        }
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      } catch (Exception e) {
        e.printStackTrace();
      }

      System.out.println("Test data read");

      sizeOfEvaluationSet = (int)((double) trainingDataFull.size() / 4);

      // turn training, evaluation, and test data into maps of examples of
      // words so we can
      // send them through Mulan
      Map<String, List<TrainingData>> trainingDataFullMap = new HashMap<String, List<TrainingData>>();
      Map<String, List<TrainingData>> testDataMap = new HashMap<String, List<TrainingData>>();

      for (TrainingData td : trainingDataFull) {
        List<TrainingData> tdList = trainingDataFullMap.get(td.getWord()
            .getWord());
        if (tdList == null) {
          tdList = new ArrayList<TrainingData>();
          tdList.add(td);
        } else {
          tdList.add(td);
        }
        trainingDataFullMap.put(td.getWord().getWord(), tdList);
      }

      for (TrainingData td : testData) {
        List<TrainingData> tdList = testDataMap.get(td.getWord().getWord());
        if (tdList == null) {
          tdList = new ArrayList<TrainingData>();
          tdList.add(td);
        } else {
          tdList.add(td);
        }
        testDataMap.put(td.getWord().getWord(), tdList);
      }

      // this can be put in a for loop to vary the window size

      int windowSize = -1; // number of words before the target and after the
      // target (x2 words total)

      String[] featureSetClassificationMethods = new String[] {
          "uniRPredictions", "uniMPredictions", "biRPredictions",
          "biMPredictions", "uniStemRPredictions", "uniStemMPredictions",
          "biStemRPredictions", "biStemMPredictions" };

      windowSize = 5;

      double[] performances = new double[featureSetClassificationMethods.length];
      double[] performancesKaggleStyle = new double[featureSetClassificationMethods.length];

      // ----- CREATE EVALUATION SET ------

      Collections.shuffle(trainingDataFull);
      List<TrainingData> evaluationData = trainingDataFull.subList(0,
          sizeOfEvaluationSet);

      // everything else becomes the real training data
      List<TrainingData> trainingData = trainingDataFull.subList(
          sizeOfEvaluationSet, trainingDataFull.size());

      // sanity check to make sure we aren't missing stuff
      System.out.println(trainingData.size() + " training data samples");
      System.out.println(evaluationData.size() + " evaluation data samples");

      Map<String, List<TrainingData>> evaluationDataMap = new HashMap<String, List<TrainingData>>();
      Map<String, List<TrainingData>> trainingDataMap = new HashMap<String, List<TrainingData>>();

      for (TrainingData td : trainingData) {
        List<TrainingData> tdList = trainingDataMap.get(td.getWord().getWord());
        if (tdList == null) {
          tdList = new ArrayList<TrainingData>();
          tdList.add(td);
        } else {
          tdList.add(td);
        }
        trainingDataMap.put(td.getWord().getWord(), tdList);
      }

      for (TrainingData td : evaluationData) {
        List<TrainingData> tdList = evaluationDataMap.get(td.getWord()
            .getWord());
        if (tdList == null) {
          tdList = new ArrayList<TrainingData>();
          tdList.add(td);
        } else {
          tdList.add(td);
        }
        evaluationDataMap.put(td.getWord().getWord(), tdList);
      }

      List<Map<String, List<List<Boolean>>>> predictions = new ArrayList<Map<String, List<List<Boolean>>>>();

      // unigrams unstemmed
      MulanFormat mulanUnigrams = new MulanFormat(trainingDataMap, windowSize,
          false, false);
      // word, list of predicted senses for each example in the order
      // given
      Map<String, List<List<Boolean>>> uniRPredictions = mulanUnigrams
          .getPredictions(evaluationDataMap, MulanFormat.RAKEL);
      predictions.add(uniRPredictions);
      Map<String, List<List<Boolean>>> uniMPredictions = mulanUnigrams
          .getPredictions(evaluationDataMap, MulanFormat.MLKNN);
      predictions.add(uniMPredictions);

      // bigrams unstemmed
      MulanFormat mulanBigrams = new MulanFormat(trainingDataMap, windowSize,
          true, false);
      // word, list of predicted senses for each example in the order
      // given
      Map<String, List<List<Boolean>>> biRPredictions = mulanBigrams
          .getPredictions(evaluationDataMap, MulanFormat.RAKEL);
      predictions.add(biRPredictions);
      Map<String, List<List<Boolean>>> biMPredictions = mulanBigrams
          .getPredictions(evaluationDataMap, MulanFormat.MLKNN);
      predictions.add(biMPredictions);

      // unigrams stemmed
      MulanFormat mulanUnigramsStem = new MulanFormat(trainingDataMap,
          windowSize, false, false);
      // word, list of predicted senses for each example in the order
      // given
      Map<String, List<List<Boolean>>> uniStemRPredictions = mulanUnigramsStem
          .getPredictions(evaluationDataMap, MulanFormat.RAKEL);
      predictions.add(uniStemRPredictions);
      Map<String, List<List<Boolean>>> uniStemMPredictions = mulanUnigramsStem
          .getPredictions(evaluationDataMap, MulanFormat.MLKNN);
      predictions.add(uniStemMPredictions);

      // bigrams stemmed
      MulanFormat mulanBigramsStem = new MulanFormat(trainingDataMap,
          windowSize, true, false);
      // word, list of predicted senses for each example in the order
      // given
      Map<String, List<List<Boolean>>> biStemRPredictions = mulanBigramsStem
          .getPredictions(evaluationDataMap, MulanFormat.RAKEL);
      predictions.add(biStemRPredictions);
      Map<String, List<List<Boolean>>> biStemMPredictions = mulanBigramsStem
          .getPredictions(evaluationDataMap, MulanFormat.MLKNN);
      predictions.add(biStemMPredictions);

      performances = EvaluationTools.getPerformances(predictions,
          evaluationDataMap);
      performancesKaggleStyle = EvaluationTools.getKagglePerformances(
          predictions, evaluationDataMap);

      double best = Double.MAX_VALUE;
      int bestIndexI = 0;
      for (int i = 0; i < performances.length; i++) {
        if (performances[i] < best) {
          best = performances[i];
          bestIndexI = i;
        }
      }

      System.out.println("Best feature set is: "
          + featureSetClassificationMethods[bestIndexI]);

      MulanFormat mulan;
      HashMap<String, Integer> index;
      switch (bestIndexI) {
      case 0:
        // uniRPredictions
        mulan = new MulanFormat(trainingDataMap, windowSize, false, false);
        index = mulan.getIndex().get(names[j]);
        break;
      case 1:
        // uniMPredictions
        mulan = new MulanFormat(trainingDataMap, windowSize, false, false);
        index = mulan.getIndex().get(names[j]);
        break;
      case 2:
        // biRPredictions
        mulan = new MulanFormat(trainingDataMap, windowSize, true, false);
        index = mulan.getIndex().get(names[j]);
        break;
      case 3:
        // biMPredictions
        mulan = new MulanFormat(trainingDataMap, windowSize, true, false);
        index = mulan.getIndex().get(names[j]);
        break;
      case 4:
        // uniStemRPredictions
        mulan = new MulanFormat(trainingDataMap, windowSize, false, true);
        index = mulan.getIndex().get(names[j]);
        break;
      case 5:
        // uniStemMPredictions
        mulan = new MulanFormat(trainingDataMap, windowSize, false, true);
        index = mulan.getIndex().get(names[j]);
        break;
      case 6:
        // biStemRPredictions
        mulan = new MulanFormat(trainingDataMap, windowSize, true, true);
        index = mulan.getIndex().get(names[j]);
        break;
      case 7:
        // biStemMPredictions
        mulan = new MulanFormat(trainingDataMap, windowSize, true, true);
        index = mulan.getIndex().get(names[j]);
        break;
      default:
        mulan = new MulanFormat(trainingDataMap, windowSize, true, true);
        index = mulan.getIndex().get(names[j]);
        break;
      }

      Set<String> indexKeySet = index.keySet();
      double bigDrop1 = 0;
      String bestKey1 = "";
      double bigDrop2 = 0;
      String bestKey2 = "";
      double bigDrop3 = 0;
      String bestKey3 = "";
      for (String key : indexKeySet) {
        Map<String, List<List<Boolean>>> thePredictions;
        switch (bestIndexI) {
        case 0:
          // uniRPredictions
          mulan = new MulanFormat(trainingDataMap, windowSize, false, false);
          thePredictions = mulan.getPredictions(evaluationDataMap,
              MulanFormat.RAKEL);
          break;
        case 1:
          // uniMPredictions
          mulan = new MulanFormat(trainingDataMap, windowSize, false, false);
          thePredictions = mulan.getPredictions(evaluationDataMap,
              MulanFormat.MLKNN);
          break;
        case 2:
          // biRPredictions
          mulan = new MulanFormat(trainingDataMap, windowSize, true, false);
          thePredictions = mulan.getPredictions(evaluationDataMap,
              MulanFormat.RAKEL);
          break;
        case 3:
          // biMPredictions
          mulan = new MulanFormat(trainingDataMap, windowSize, true, false);
          thePredictions = mulan.getPredictions(evaluationDataMap,
              MulanFormat.MLKNN);
          break;
        case 4:
          // uniStemRPredictions
          mulan = new MulanFormat(trainingDataMap, windowSize, false, true);
          thePredictions = mulan.getPredictions(evaluationDataMap,
              MulanFormat.RAKEL);
          break;
        case 5:
          // uniStemMPredictions
          mulan = new MulanFormat(trainingDataMap, windowSize, false, true);
          thePredictions = mulan.getPredictions(evaluationDataMap,
              MulanFormat.MLKNN);
          break;
        case 6:
          // biStemRPredictions
          mulan = new MulanFormat(trainingDataMap, windowSize, true, true);
          thePredictions = mulan.getPredictions(evaluationDataMap,
              MulanFormat.RAKEL);
          break;
        case 7:
          // biStemMPredictions
          mulan = new MulanFormat(trainingDataMap, windowSize, true, true);
          thePredictions = mulan.getPredictions(evaluationDataMap,
              MulanFormat.MLKNN);
          break;
        default:
          mulan = new MulanFormat(trainingDataMap, windowSize, true, true);
          thePredictions = mulan.getPredictions(evaluationDataMap,
              MulanFormat.MLKNN);
          break;
        }

        List<Map<String, List<List<Boolean>>>> toEval = new ArrayList<Map<String, List<List<Boolean>>>>();
        toEval.add(thePredictions);
        double[] perf = EvaluationTools.getKagglePerformances(toEval,
            evaluationDataMap);

        if (perf[0] > bigDrop1) {
          bigDrop3 = bigDrop2;
          bestKey3 = bestKey2;
          bigDrop2 = bigDrop1;
          bestKey2 = bestKey1;
          bigDrop1 = perf[0];
          bestKey1 = key;
        } else if (perf[0] > bigDrop2) {
          bigDrop3 = bigDrop2;
          bestKey3 = bestKey2;
          bigDrop2 = perf[0];
          bestKey2 = key;
        } else if (perf[0] > bigDrop3) {
          bigDrop3 = perf[0];
          bestKey3 = key;
        }
      }

      System.out.println("Best Features for " + names[j]);
      System.out.println("1. " + bestKey1);
      System.out.println("2. " + bestKey2);
      System.out.println("3. " + bestKey3);
    }
  }

  public static void createKaggleOutput(
      Map<String, List<List<Boolean>>> testPredictions,
      List<TrainingData> originalTestData) {
    BufferedWriter bw;
    try {
      bw = new BufferedWriter(new FileWriter("testSetPredictions_kaggle.txt"));
      int numRows = 0;
      String currentKey = null;
      for (TrainingData td : originalTestData) {
        String key = td.getWord().getWord();

        if (key.equals(currentKey)) {
          // we've already dealt with it so...
          continue;
        }

        for (List<Boolean> senseList : testPredictions.get(key)) {
          for (Boolean b : senseList) {
            if (Boolean.TRUE.equals(b)) {
              bw.append("1");
              bw.newLine();
            } else {
              bw.append("0");
              bw.newLine();
            }
            numRows++;
          }
        }

        currentKey = key;
      }

      // for(String a: authorTestPredictions){
      // bw.write(a);
      // bw.newLine();
      // }
      bw.close();
      System.out.println("Num rows: " + numRows);
    } catch (IOException e) {
      System.out.println("IOException thrown writing output file.");
      System.exit(1);
    }

    return;
  }

}
